iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0

在前面的章節中,我們花了許多時間探討 GraphQL 及 Strawberry,這主要是希望讓大家對於 GraphQL 擁有基礎的理解,並對 Strawberry 這個函式庫的功能有所認識。我決定先不將 Django 與 GraphQL 混合在一起,以避免在學習過程中同時需要處理太多的資訊。透過這種方式,大家可以在理解了 GraphQL 的基礎之後,再逐步學習如何結合 Django 進行開發,使學習過程更加順暢與清晰。

接下來,我們將著手建立一個新的 Python 開發環境,並且安裝 Strawberry 與 Django 相關的 Python 套件。我們將示範如何建立一個簡單的 Django 應用程式,實作一個部落格系統。最後,我們將顯示如何將部落格的 ORM 模型,以簡單而直觀的方式轉換成 GraphQL 查詢 API。

建立 Python 專案

首先我們透過 Poetry 建立新的專案開發環境,基本上跟前面的練習環境差不多,如果不清楚的話,可以參考前面 Day 2:安裝 Strawberry 環境 [1]:

$ mkdir django-graphql-tutorial
$ cd django-graphql-tutorial
$ pyenv local 3.11.3
$ poetry init

下面是poetry init的建立專案的過程,為了方便說明,過程中跳過安裝套件:

This command will guide you through creating your pyproject.toml config.

Package name [django-graphql-tutorial]:
Version [0.1.0]:
Description []:
Author [username <username@email.com>, n to skip]:
License []:
Compatible Python versions [^3.11]:

Would you like to define your main dependencies interactively? (yes/no) [yes] no
Would you like to define your development dependencies interactively? (yes/no) [yes] no
Generated file

[tool.poetry]
name = "django-graphql-tutorial"
version = "0.1.0"
description = ""
authors = ["username <username@email.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.11"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Do you confirm generation? (yes/no) [yes]

做完上面的步驟後,當前目錄下,會有以下檔案

.
├── .python-version
└── pyproject.toml

安裝套件

前面的操作,我們使用 Poetry 建立專案,接著就我們的專案的套件管理也交給 Poetry 管理。

$ poetry add django strawberry_graphql_django

執行結果如下:

Using python3 (3.11.3)
Creating virtualenv django-graphql-tutorial-6PNY0bHT-py3.11 in /Users/username/Library/Caches/pypoetry/virtualenvs
Using version ^4.2.5 for django
Using version ^0.17.4 for strawberry-graphql-django

Updating dependencies
Resolving dependencies... (0.7s)

Package operations: 9 installs, 0 updates, 0 removals

  • Installing six (1.16.0)
  • Installing asgiref (3.7.2)
  • Installing graphql-core (3.2.3)
  • Installing python-dateutil (2.8.2)
  • Installing sqlparse (0.4.4)
  • Installing typing-extensions (4.8.0)
  • Installing django (4.2.5)
  • Installing strawberry-graphql (0.209.2)
  • Installing strawberry-graphql-django (0.17.4)

Writing lock file

接下來安裝開發環境所需的套件,這邊先安裝ipython,在使用 Django Shell 的介面的時候它提供更好地 Python Console 互動介面。

$ poetry add ipython --group dev

Poetry 提供相依套件群組的功能,讓我們針對不同需求所使用的套件進行分組,這個功能讓我們可以在部署應用程式的時候安裝最少需要的套件,這麼做最有感的應該是安裝速度變快與佔用較少的磁碟空間。

再接下來安裝跟型別、格式化與 Lint 等跟程式碼檢查相關的套件:

$ poetry add django-types ruff black pyright --group lint

更多的相關說明可以參考友情連結到 Day03 - 開發輔助工具設定[2] 與 Day04 - 初探 DRF[3]。

再來我們直接使用 Day03 - 開發輔助工具設定 的 ruff 跟 pyright 的設定檔,直接加到pyproject.toml最後面:

[tool.ruff]
target-version = "py311"
select = [
  # pyflakes
  "F",
  # pycodestyle
  "E",
  "W",
  # pep8-naming
  "N",
  # pylint
  "PL",
  # mccabe
  "C90",
  # isort
  "I",
  # pydocstyle
  "D",
  # pyupgrade
  "UP",
  # flake8-builtins
  "A",
  # flake8-commas
  "COM",
  # flake8-bugbear
  "B",
  # flake8-comprehensions
  "C4",
  # flake8-type-checking
  "TCH",
  # flake8-datetimez
  "DTZ",
  # flake8-print
  "T20",
  # flake8-tidy-imports
  "TID",
  # flake8-simplify
  "SIM",
  # flake8-quotes
  "Q",
  # flake8-use-pathlib
  "PTH",
  # flake8-import-conventions
  "ICN",
  # flake8-django
  "DJ",
]
ignore = [
  # pydocstyle: Do not require any docstring
  "D100",
  "D101",
  "D102",
  "D103",
  "D104",
  "D105",
  "D106",
  "D107",
  "D212",
  "D203",
  # pydocstyle: Allow blank line after docstring
  "D202",
]

[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "all"

[tool.ruff.pydocstyle]
convention = "google"

[tool.pyright]
pythonVersion = "3.11"
typeCheckingMode = "basic"
reportUnnecessaryTypeIgnoreComment = true

到這邊就完成 Python 專案環境的設定。

建立 Django 專案

使用 django-admin指令建立 Django 專案:

$ django-admin startproject server .

在當前目錄下.,建立一個叫serverDjango 專案,執行完成後結果如下:

.
├── .python-version
├── manage.py
├── poetry.lock
├── pyproject.toml
└── server
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

Django 專案建立完成後,我通常會建立一個 Python 模組叫app用來放自己的Django應用程式:

$ mkdir -p server/app/
$ touch server/app/__init__.py

再接下來透過django-admin指令建立部落格應用程式的模組:

$ cd server/app/
$ django-admin startapp blog

執行完成後,django-graphql-tutorial資料夾下目錄結構如下:

.
├── .python-version
├── manage.py
├── poetry.lock
├── pyproject.toml
└── server
    ├── __init__.py
    ├── app
    │   ├── __init__.py
    │   └── blog
    │       ├── __init__.py
    │       ├── admin.py
    │       ├── apps.py
    │       ├── migrations
    │       │   └── __init__.py
    │       ├── models.py
    │       ├── tests.py
    │       └── views.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

再建立一個utils模組放共用的程式:

$ cd django-graphql-tutorial
$ mkdir -p server/utils/
$ touch server/utils/__init__.py

接下來我會建立幾個 Django 共用的 Base Model:

$ mkdir -p server/utils/django
$ touch server/utils/django/__init__.py
$ touch server/utils/django/models.py

這邊編輯server/utils/django/models.py

import uuid
from django.db import models


__all__ = [
    "UUIDModel",
    "TimestampModel",
    "BaseModel",
]


class UUIDModel(models.Model):
    id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)

    class Meta:
        abstract = True
      

class TimestampModel(models.Model):
    created_at = models.DateTimeField("建立時間", auto_now_add=True, editable=False)
    motified_at = models.DateTimeField("修改時間", auto_now=True, editable=False)

    class Meta:
        abstract = True


class BaseModel(UUIDModel, TimestampModel):
    class Meta:
        abstract = True
  • UUIDModel:是將 Django 預設的 Model id 型態改成UUID
  • TimestampModel:增加建立時間跟修改時間的時間戳記欄位。
  • BaseModel:自己的專案內應用程式用的共用基礎模型。

到這邊就完成專案的基本結構。

Lint 與 Format 專案

首先先使用ruff做檢查:

$ cd django-graphql-tutorial
$ ruff check --fix .
server/settings.py:89:89: E501 Line too long (91 > 88 characters)
server/utils/django/models.py:13:5: A003 Class attribute `id` is shadowing a Python builtin
Found 2 errors.

會看到有兩個錯誤要修正:

# server/settings.py
# ... 省略
AUTH_PASSWORD_VALIDATORS = [
    {
-       "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
+       "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",  # noqa: E501
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]
# ... 省略
# server/utils/django/models.py
# ... 省略
class UUIDModel(models.Model):
-		id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
+   id = models.UUIDField(  # noqa: A003
+       primary_key=True,
+       editable=False,
+       default=uuid.uuid4,
+   )

    class Meta:
        abstract = True
# ... 省略

然後使用black自動排版:

$ black .
reformatted /django-graphql-tutorial/server/app/blog/admin.py
reformatted /django-graphql-tutorial/server/app/blog/views.py
reformatted /django-graphql-tutorial/server/app/blog/models.py
reformatted /django-graphql-tutorial/server/app/blog/tests.py

All done! ✨ 🍰 ✨
4 files reformatted, 13 files left unchanged.

最後使用pyright做型別檢查:

$ pyright .

最終django-graphql-tutorial目錄結構如下:

.
├── .python-version
├── manage.py
├── poetry.lock
├── pyproject.toml
└── server
    ├── __init__.py
    ├── app
    │   ├── __init__.py
    │   └── blog
    │       ├── __init__.py
    │       ├── admin.py
    │       ├── apps.py
    │       ├── migrations
    │       │   └── __init__.py
    │       ├── models.py
    │       ├── tests.py
    │       └── views.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    ├── utils
    │   ├── __init__.py
    │   └── django
    │       ├── __init__.py
    │       └── models.py
    └── wsgi.py

完整專案可以參考 Github https://github.com/JiaWeiXie/django-graphql-tutorial.git

參考資料


上一篇
Day 12:Strawberry 的其他功能
下一篇
Day 14:簡單的部落格應用
系列文
Django 與 Strawberry GraphQL:探索現代 API 開發之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言